#
# Copyright 2010 Steve Leach
# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
#

import os
import logging

from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
from google.appengine.ext import db
from google.appengine.api import users
from google.appengine.runtime import DeadlineExceededError
from google.appengine.api import memcache
from google.appengine.datastore import entity_pb

from eveoperation import EveOperation, Pilot, Corp, AssistType
from evedata import EveData, get_eve_data

class EveOpsHandler(webapp.RequestHandler):
	"""
	Generic http handler support.  
	Deprecated - extend BaseHandler instead.
	"""
	def display_page(self, page, values):
  		path = os.path.join(os.path.dirname(__file__), page)
  		self.response.out.write(template.render(path, values))

	def authorised(self, eve_data):
		#if not eve_data.char_name:
		#	eve_data = get_eve_data(eve_data)

		if eve_data.char_name == None or eve_data.char_name == '{unknown}':
			self.redirect('/unauthorized?reason=id')
			return False

		corp = Corp.gql('WHERE corp_id = :1', eve_data.corp_id)
		if corp.count() > 0:
			corp = corp.get()
		elif eve_data.corp_id:
			corp = Corp(corp_name=eve_data.corp_name)
			corp.corp_id = eve_data.corp_id
			corp.put()

		pilot = Pilot.gql('WHERE char_name = :1', eve_data.char_name).get()
		if not pilot:
			pilot = Pilot()
			pilot.char_name = eve_data.char_name
			pilot.char_id = eve_data.char_id
			pilot.corp_id = eve_data.corp_id
			pilot.put()

		if not corp.prime:
			self.redirect('/unauthorized?reason=corp')
			return False

		return True

	def get_op(self):
		"""
		Checks the http request params to try and identify which eve operation
		should be used.
		"""
		if self.request.get('key'):
			op = db.get(self.request.get('key'))
		elif self.request.get('id'):
			op = EveOperation.get_by_id(int(self.request.get('id')))
		return op

class BaseHandler(EveOpsHandler):
	def get(self):
		try:
			self.eve_data = get_eve_data(self.request.headers)
			if self.auth_only():
				if not self.authorised(self.eve_data):
					return

			self.get_standard_data()

			self.set_standard_pagevalues()

			self.do_get()

		except DeadlineExceededError:
			logging.error('Deadline exceeded error in ' + self.request.path + ' for ' + self.eve_data.char_name)
			
			self.response.out.write('<html><head>')
			self.response.out.write('<link rel="stylesheet" type="text/css" href="static/ingame.css" />')
			self.response.out.write('<meta http-equiv="refresh" content="30" >')
			self.response.out.write('</head><body>')
			self.response.out.write('<h1>Eve Ops Tracker - Error</h1>')
			self.response.out.write('<p  class="error">Uh-oh, someone forgot to feed the hampsters</p>')
			self.response.out.write('</body></html>')

	def do_get(self):
		pass

	def post(self):
		self.eve_data = get_eve_data(self.request.headers)
		if self.auth_only():
			if not self.authorised(self.eve_data):
				return	

		self.get_standard_data()

		self.set_standard_pagevalues()

		self.do_post()

	def do_post(self):
		pass

	def auth_only(self):
		"""
		Subclasses should override to return False if resource can be
		used by unauthorised users.  Default is True.
		"""
		return True

	def get_standard_data(self):
		if self.request.get('op_id'):
			self.op = EveOperation.get_by_id(int(self.request.get('op_id')))

	def set_standard_pagevalues(self):
		self.pagevalues = {}
		self.pagevalues['eve_data'] = self.eve_data
		self.pagevalues['headers'] = self.request.headers
		self.pagevalues['logon_url'] = users.create_login_url('/')

		stats = memcache.get_stats()
		if stats:
			self.pagevalues['cache_items'] = stats['items']
			if (stats['hits']+stats['misses']) > 0:
				self.pagevalues['cache_ratio'] = 100.0 * stats['hits'] / (stats['hits']+stats['misses'])

	def show_page(self, page):
  		path = os.path.join(os.path.dirname(__file__), page)
  		self.response.out.write(template.render(path, self.pagevalues))

	def current_pilot_name(self):
		return self.eve_data.char_name


def serialize_entities(models):
	"""
	Convert entities into a byte stream that can be cached.
	"""
	# From http://blog.notdot.net/2009/9/Efficient-model-memcaching
	if models is None:
		return None
	elif isinstance(models, db.Model):
		# Just one instance
		return db.model_to_protobuf(models).Encode()
	else:
		# A list
		return [db.model_to_protobuf(x).Encode() for x in models]

def deserialize_entities(data):
	"""
	Convert a byte stream (from a cache, for example) into entities.
	"""
	# From http://blog.notdot.net/2009/9/Efficient-model-memcaching
	if data is None:
		return None
	elif isinstance(data, str):
		# Just one instance
		return db.model_from_protobuf(entity_pb.EntityProto(data))
	else:
		return [db.model_from_protobuf(entity_pb.EntityProto(x)) for x in data]

def get_cached_query(key):
	return deserialize_entities(memcache.get(key))

def cache_query(fetched, key, timeout):
	memcache.set(key, serialize_entities(fetched), timeout)

class SimplePageHandler(BaseHandler):
	"""
	Http request handler for displaying simple pages.
	"""
	def do_get(self):  
		self.show_page(self.request.get('page') + '.html')

class PublicPageHandler(BaseHandler):
	"""
	Http request handler for displaying simple pages.
	"""
	def do_get(self):  
		self.show_page(self.request.get('page') + '.html')

	def auth_only(self):
		return False

class UnsupportedHandler(BaseHandler):
	def do_get(self):  
		self.show_page('unsupported.html')

class FormArgsHandler(webapp.RequestHandler):
	def get(self):
		print ''
		return self.post()

	def post(self):
		for arg in self.request.arguments():
			print arg, '=', self.request.get(arg)

class EveInfoHandler(BaseHandler):
	def auth_only(self):
		return False

	def do_get(self):  	
		text = ''	  			
		for key in self.request.headers.keys():
			text = text + key
			text = text + ' = '
			text = text + self.request.headers[key]
			text = text + '\n'

		self.pagevalues['headertext'] = text

		char_name_code = ''
		for c in self.eve_data.char_name:
			char_name_code = char_name_code + c + ':' + str(ord(c)) + ' '
		self.pagevalues['char_name_code'] = char_name_code.strip()

		self.pagevalues['logon_url'] = users.create_login_url('/')

		self.show_page('eveinfo.html')

class ErrorHandler(BaseHandler):
	"""
	Generic error page handler.
	"""
	errors = {
		'1001': 'Pilot already on operation',
		'1002': 'Operation key not found',
		'1003': 'Site not found',
		'1004': 'Operation is locked',
		'1005': 'You are not the FC',
		'9999': 'Unexpected error'
		}

	def auth_only(self):
		return False

	def do_get(self):  
		msgid = self.request.get('msg')
		if msgid in self.errors:
			msg = self.errors[msgid]
		else:
			msg = 'System error ' + msgid

		self.pagevalues['msgid'] = msgid
		self.pagevalues['msg'] = msg

	  	self.show_page('error.html')

class DevInitHandler(BaseHandler):
	"""
	Http request handler for initialising the server datastore.
	"""

	def auth_only(self):
		return False

	def do_post(self):
		pilot = Pilot()
		pilot.char_name = self.request.get('charName')
		pilot.char_id = self.request.get('charID')	
		pilot.corp_id = self.request.get('corpID')
		pilot.google_account = users.get_current_user().email()		
		pilot.put()

		corp = Corp()
		corp.prime = True
		corp.corp_name = self.request.get('corpName')
		corp.corp_id = self.request.get('corpID')
		corp.put()

		self.redirect('/listops')

class ConfirmHandler(BaseHandler):

	def do_get(self):
		self.pagevalues['action'] = self.request.get('action')
		self.pagevalues['keyval'] = self.request.get('key')
		if self.pagevalues['action'] == '/deleteop':
			self.pagevalues['prompt'] = 'Delete operation.'
		elif self.pagevalues['action'] == '/sigcleanup':
			self.pagevalues['prompt'] = 'Cleanup will delete all sigs more than 2 days old.'
		else:
			self.pagevalues['prompt'] = 'You are about to do something dangerous.'
	  	self.show_page('confirm.html')

def init_assist_type():
	if AssistType.all().count() == 0:
		atype = AssistType(name='Unknown')
		atype.put()
		result = 'Initiated'
	else:
		result = 'Already initiated'
	return result

def init_corp_values(corp_name):
	corp = Corp.gql('WHERE corp_id = :1', corp_name).get()
	if corp:
		logging.info(corp.corp_name)
		if not corp.ave_op_isk:
			corp.ave_op_isk = 0
			corp.op_tax_rate = 0.0
			corp.op_fc_bonus = 0.0
			corp.put()

class TestHandler(BaseHandler):
	"""
	Handler for ad-hoc developer testing.
	
	Hack this around for whatever purpose is required as a short-term fix.
	"""
	def do_get(self):

		result = init_assist_type()

		init_corp_values(self.eve_data.corp_id)

		logging.info('TestHandler: ' + result)

		self.pagevalues['msg'] = 'TestHandler: ' + result

	  	self.show_page('error.html')

class WebSite(db.Model):
	url = db.StringProperty(required=True)
	summary = db.StringProperty()

class LinksHandler(BaseHandler):
	def do_get(self):
		sites = WebSite.all()
		if sites.count() == 0:
			WebSite(url='http://www.eveonline.com/',summary='Eve Online').put()
			sites = WebSite.all()
		self.pagevalues['links'] = sites
		self.show_page('links.html')

def add_base_mappings(mappings):
	"""
	Adds the mappings for the generic handlers.
	"""
  	mappings.append(('/pages', SimplePageHandler))
  	mappings.append(('/pubpage', PublicPageHandler))
  	mappings.append(('/unsupported', UnsupportedHandler))
  	mappings.append(('/eveinfo', EveInfoHandler))
  	mappings.append(('/error', ErrorHandler))
	mappings.append(('/devinit', DevInitHandler))
	mappings.append(('/showargs', FormArgsHandler))
	mappings.append(('/confirm', ConfirmHandler))
	mappings.append(('/devtest', TestHandler))
	mappings.append(('/links', LinksHandler))

